package br.ufpe.cin.mac.middleware.connectors.abstracts;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import br.ufpe.cin.mac.middleware.connectors.AbstractConnector;
import br.ufpe.cin.mac.middleware.exceptions.InvalidOperationException;
import br.ufpe.cin.mac.util.Constants;
import br.ufpe.cin.mac.util.Message;

/**
 * Class Reliable
 * 
 * @author Osmany
 * @author Rafael Roque
 * 
 */
public class Reliable extends AbstractConnector implements ReliableMBean {

	public Reliable(String compFrom, String compTo, String connConcrete) {
		super(Reliable.class.getCanonicalName(), compFrom, compTo, connConcrete);
	}

	/***************************************************************************
	 * ******************* INTERNAL METHODS ********************** * *
	 */

	protected void msgManager(Message msg) throws InvalidOperationException {
		List<String> ops = msg.readOperations();
		Iterator<String> it = ops.iterator();
		while (it.hasNext()) {
			String op = (String) it.next();

			if (op.equals(Constants.OP_REQUEST))
				sendRequest(msg);

			else if (op.equals(Constants.OP_RESPONSE))
				sendResponse(msg);

			else if (op.equals(Constants.OP_SEND_PROXY))
				sendProxy(msg);

			else {
				throw new InvalidOperationException(op, this.getConnectorName());
			}
		}
	}

	/**
	 * Ida do pacote
	 * 
	 * Entrada em InvC Sa�da em InvS
	 * 
	 * @param msg
	 */
	private void sendRequest(Message msg) {
		msg.setConnector("Middleware:Type=AbstractConnector,name="
				+ this.getClass().getSimpleName());
		msg.removeAllInterceptors();
		loadInterceptors(msg, Constants.REQUEST);

		this.invS.put(msg);

		sendNotification(msg.hashCode(), Constants.PORT_INVS);
	}

	/**
	 * Volta do pacote Entrada em TerS Saida em TerC
	 * 
	 * @param msg
	 */
	private void sendResponse(Message msg) {
		//msg.removeAllInterceptors();
		// loadInterceptors(msg, Constants.RESPONSE);
		makeResponse(msg);
		this.invS.put(msg);

		sendNotification(msg.hashCode(), Constants.PORT_INVS);
	}

	private void sendProxy(Message msg) {
		//	msg.removeAllInterceptors();
		List<String> ops = new LinkedList<String>();

		if (msg.getType().equals(Constants.REQUEST)) {
			ops.add(Constants.OP_REQUEST); // Pode mudar para receiveRequest

			this.invS.put(msg);
			sendNotification(msg.hashCode(), Constants.PORT_INVS);

		} else if (msg.getType().equals(Constants.RESPONSE)) {
			ops.add(Constants.OP_RESPONSE); // Pode mudar para receiveResponse

			this.terC.put(msg);
			sendNotification(msg.hashCode(), Constants.PORT_TERC);
		}

		msg.addInterceptor("Proxy", ops);
	}

	private void loadInterceptors(Message msg, String type) {
		msg.removeAllInterceptors();
		msg.setIndex(0);
		List<String> ops = new LinkedList<String>();
		String connConcrete = "";

		if (type == Constants.REQUEST) {
			connConcrete = this.connConcrete.replaceAll("Server", "Client");
			ops.add("sendRequest");
		} else {
			connConcrete = this.connConcrete.replaceAll("Client", "Server");
			ops.add("sendResponse");
		}

		/* Adiciona os componentes e conectores */

		msg.addInterceptor(this.compFrom, ops);
		msg.addInterceptor(this.compTo, ops);
		msg.addInterceptor(connConcrete, ops);
		msg.addInterceptor(backComponent(this.compTo), ops);
		msg.addInterceptor(backComponent(this.compFrom), ops);
		/* Por fim o Proxy */

		ops = new LinkedList<String>();
		ops.add(Constants.OP_SEND_PROXY);
		msg.addInterceptor("Middleware:Type=AbstractConnector,name="
				+ this.getClass().getSimpleName(), ops);
	}

	private void makeResponse(Message msg) {
		//System.err.println();
		msg.getInterceptors().remove(msg.getInterceptors().size()-1);
		msg.removeOperators();
		List<String> interceptors = msg.getInterceptors();
		List<String> ops = new LinkedList<String>();
		
		ops.add("sendResponse");

		String connConcrete = interceptors.remove(2).replaceAll("Client", "Server");
		interceptors.add(2,connConcrete);
		for (int i = 0; i < interceptors.size()-1; i++) {
			msg.addOperation(ops);
		}
		ops = new LinkedList<String>();
		ops.add(Constants.OP_SEND_PROXY);
		msg.addOperation(ops);
		msg.setIndex(0);
	}

	private String backComponent(String component) {
		if (component.equals(Constants.COMP_CRYPT))
			return Constants.COMP_DECRYPT;
		else
			return component;
	}

	public void setHost(String host) {
		// TODO Auto-generated method stub
		
	}
}